package top.hcode.hoj.dao.org;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alicp.jetcache.anno.Cached;
import com.baomidou.mybatisplus.extension.service.additional.query.impl.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.sagacity.sqltoy.model.MapKit;
import org.springframework.stereotype.Service;
import top.hcode.hoj.common.LazyDao;
import top.hcode.hoj.mapper.OrgMapper;
import top.hcode.hoj.pojo.entity.org.Org;
import top.hcode.hoj.utils.Constants;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author orangej
 * @since 2025-01-20
 */
@Service
@RequiredArgsConstructor
public class OrgDao extends ServiceImpl<OrgMapper, Org> {

    private final LazyDao dao;

    /**
     * 根据treeCode查询下级数据
     *
     * @param parentTreeCode 父节点编码
     * @param orgType        组织类型
     */
    public List<Org> queryChildrenByTreeCode(String parentTreeCode, Constants.OrgType orgType) {
        if (Strings.isBlank(parentTreeCode)) {
            return new ArrayList<>();
        }

        return queryChildrenByTreeCode(Lang.list(parentTreeCode), orgType);
    }

    /**
     * 根据treeCode查询下级数据
     *
     * @param parentTreeCodeList 父节点编码列表
     * @param orgType            组织类型
     */
    public List<Org> queryChildrenByTreeCode(List<String> parentTreeCodeList, Constants.OrgType orgType) {
        if (Lang.isEmpty(parentTreeCodeList)) {
            return new ArrayList<>();
        }

        LambdaQueryChainWrapper<Org> query = lambdaQuery();

        query.and(q -> {
            for (String code : parentTreeCodeList) {
                q.or().likeRight(Org::getTreeCode, code);
            }
        });

        return query.eq(Org::getOrgType, orgType.getType())
                .orderByAsc(Org::getTreeCode)
                .list();
    }

    public List<String> queryChildrenIdByTreeCode(List<String> parentTreeCodeList, Constants.OrgType orgType) {
        if (Lang.isEmpty(parentTreeCodeList)) {
            return new ArrayList<>();
        }

        LambdaQueryChainWrapper<Org> query = lambdaQuery();

        query.and(q -> {
            for (String code : parentTreeCodeList) {
                q.or().likeRight(Org::getTreeCode, code);
            }
        });

        return query.select(Org::getId)
                .eq(Org::getOrgType, orgType.getType())
                .orderByAsc(Org::getTreeCode)
                .list()
                .stream().map(Org::getId).collect(Collectors.toList());
    }

    public Integer queryClassStudentCount(String classId) {
        return dao.selectOne(Integer.class, "queryClassStudentCount", MapKit.map("classId", classId));
    }

    public Integer queryClassStudentCount(List<String> classIdList) {
        return dao.selectOne(Integer.class, "queryClassStudentCount", MapKit.map("classIdList", classIdList));
    }

    /**
     * 查询学校的学生数量，先查询出学校的所有班级，然后查询所有班级的学生数量
     *
     * @param orgId 可以是学校、校区、年级等，只要是班级的上层
     * @return 学生数量
     */
    public Integer queryOrgStudentCount(String orgId) {
        List<String> classIdList = queryClassIdOf(orgId);
        return queryClassStudentCount(classIdList);
    }

    public Integer queryOrgStudentCount(List<String> orgIdList) {
        List<String> classIdList = queryClassIdOf(orgIdList);
        return queryClassStudentCount(classIdList);
    }

    /**
     * 查询org下的班级id列表
     *
     * @param orgId 可以是学校、校区、年级等，只要是班级的上层
     * @return 班级id列表
     */
    public List<String> queryClassIdOf(String orgId) {
        List<String> treeCodeList = queryTreeCodeList(Lang.list(orgId));
        return queryChildrenIdByTreeCode(treeCodeList, Constants.OrgType.CLASS);
    }

    /**
     * 根据班级、年级、学校id查询班级id列表
     * 优先级：班级 > 年级 > 学校
     *
     * @param classId      班级id
     * @param gradeId      年级id
     * @param schoolId     学校id
     * @param schoolIdList 学校id列表
     * @return 查询条件为空时返回空列表
     */
    public List<String> queryClassIdOf(String classId, String gradeId, String schoolId, List<String> schoolIdList) {
        if (Strings.isNotBlank(classId)) {
            return Lang.list(classId);
        } else if (Strings.isNotBlank(gradeId)) {
            return queryClassIdOf(gradeId);
        } else if (Strings.isNotBlank(schoolId)) {
            return queryClassIdOf(schoolId);
        } else if (Lang.isNotEmpty(schoolIdList)) {
            return queryClassIdOf(schoolIdList);
        } else {
            return new ArrayList<>();
        }
    }

    public List<String> queryClassIdOf(List<String> orgIdList) {
        if (Lang.isEmpty(orgIdList)) {
            return new ArrayList<>();
        }
        CollUtil.filter(orgIdList, Strings::isNotBlank);
        if (Lang.isEmpty(orgIdList)) {
            return new ArrayList<>();
        }

        List<String> treeCodeList = queryTreeCodeList(orgIdList);
        return queryChildrenIdByTreeCode(treeCodeList, Constants.OrgType.CLASS);
    }

    public List<Org> querySchoolOfUser(String userId) {
        return dao.selectList(Org.class, "select * from org where tree_code in " +
                                         "(select distinct substr(tree_code,1,4) from org_has_user where user_id = ?)", userId);
    }

    public List<Org> queryCampusOfUser(String userId) {
        return dao.selectList(Org.class, "select * from org where id in " +
                                         "(select distinct id from org_has_user where user_id = ? and org_type = 3)", userId);
    }

    @Cached
    public List<Org> queryAllSchool() {
        return lambdaQuery().eq(Org::getOrgType, Constants.OrgType.SCHOOL.getType())
                .orderByAsc(Org::getTreeCode).list();
    }

    @Cached
    public Map<String, Org> queryAllSchoolMap() {
        return queryAllSchool().stream().collect(Collectors.toMap(Org::getId, it -> it));
    }

    public List<Org> queryAllCampus() {
        return lambdaQuery().eq(Org::getOrgType, Constants.OrgType.CAMPUS.getType())
                .orderByAsc(Org::getTreeCode).list();
    }

    public List<String> queryTreeCodeList(List<String> orgIdList) {
        if (CollUtil.isEmpty(orgIdList)) {
            return new ArrayList<>();
        }
        return lambdaQuery().select(Org::getTreeCode)
                .in(Org::getId, orgIdList)
                .list()
                .stream().map(Org::getTreeCode).collect(Collectors.toList());
    }

    @Cached
    public String getOrgNameById(String orgId) {
        return dao.fetchString("select org_name from org where id = ?", orgId);
    }

    public Map<String, String> queryClassIdCampusNameMap(List<String> classIdList) {
        return dao.findBySqlToMap("queryClassIdCampusName",
                NutMap.NEW().addv("classIdList", new HashSet<>(classIdList)),
                NutMap.class,
                it -> it.getString("id"),
                it -> it.getString("className"));
    }

    public String queryChildMaxTreeCode(String id) {
        if (id == null) {
            return dao.fetchString("select max(tree_code) from org where parent_id is null");
        }

        return dao.fetchString("select max(tree_code) from org where parent_id = ?", id);
    }

    public int countByTreeCode(String treeCode) {
        return ObjectUtil.defaultIfNull(dao.fetchInt("select count(1) from org where tree_code = ?", treeCode), 0);
    }

    @Cached
    public Org getSchoolByName(String schoolName) {
        return dao.selectOne(Org.class, "select * from org where org_name = ? and org_type = 2", schoolName);
    }

    @Cached
    public Org getGradeByCampusTreeCodeAndGradeNum(String treeCode, int gradeNum) {
        System.out.println("getGradeBySchoolTreeCodeAndGradeNum" + treeCode + " " + gradeNum);
        return dao.selectOne(Org.class, "select * from org where " +
                                        "tree_code like ? and org_type = 6 and grade_num = ?",
                treeCode + "%", gradeNum);
    }

    @Cached
    public Org getClassByGradeClassNum(String gradeId, int classNum) {
        return dao.selectOne(Org.class, "select * from org where " +
                                        "parent_id = ? and org_type = 7 and class_num = ?",
                gradeId, classNum);
    }

    @Cached
    public Org getCampusBySchoolAndCampusName(String id, String campusName) {
        return dao.selectOne(Org.class, "select * from org where " +
                                        "parent_id = ? and org_type = 3 and org_name = ?",
                id, campusName);
    }

    public Org getByTreeCode(String treeCode) {
        return dao.selectOne(Org.class, "select * from org where tree_code = ?", treeCode);
    }

    public Org getFirstSemester(String treeCode) {
        return dao.selectOne(Org.class,
                "select * from org where " +
                "tree_code like ? and org_type = 5 order by tree_code limit 1", treeCode + "%");
    }

    public Org getGradeBySchoolTreeCodeAndGradeNum(String treeCode, Integer gradeNum) {
        return dao.selectOne(Org.class, "select * from org where " +
                "tree_code like ? and org_type = 6 and grade_num = ?", treeCode + "%", gradeNum);
    }
}
